/*
 * SPDX-FileCopyrightText: 2006 Peter Penz (peter.penz@gmx.at) and Patrice Tremblay
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "statusbarspaceinfo.h"

#include "spaceinfoobserver.h"

#include <KLocalizedString>
#include <KNS3/KMoreToolsMenuFactory>
#include <math.h>

#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>

#define ROUND_MARGIN 6
#define VERTICAL_SPACING 1

static const int LightShade = 100;
static const int MidShade = 200;
static const int DarkShade = 300;

StatusBarSpaceInfo::StatusBarSpaceInfo(QWidget* parent) :
    KCapacityBar(KCapacityBar::DrawTextOutline, parent),
    m_observer(nullptr)
{
    setCursor(Qt::PointingHandCursor);
}

StatusBarSpaceInfo::~StatusBarSpaceInfo()
{
}

void StatusBarSpaceInfo::setShown(bool shown)
{
    m_shown = shown;
    if (!m_shown) {
        hide();
        m_ready = false;
    }
}

void StatusBarSpaceInfo::setUrl(const QUrl& url)
{
    if (m_url != url) {
        m_url = url;
        m_ready = false;
        if (m_observer) {
            m_observer.reset(new SpaceInfoObserver(m_url, this));
            connect(m_observer.data(), &SpaceInfoObserver::valuesChanged, this, &StatusBarSpaceInfo::slotValuesChanged);
        }
    }
}

QUrl StatusBarSpaceInfo::url() const
{
    return m_url;
}

void StatusBarSpaceInfo::update()
{
    if (m_observer) {
        m_observer->update();
    }
}

void StatusBarSpaceInfo::showEvent(QShowEvent* event)
{
    if (m_shown) {
        if (m_ready) {
            KCapacityBar::showEvent(event);
        }

        if (m_observer.isNull()) {
            m_observer.reset(new SpaceInfoObserver(m_url, this));
            connect(m_observer.data(), &SpaceInfoObserver::valuesChanged, this, &StatusBarSpaceInfo::slotValuesChanged);
        }
    }
}

void StatusBarSpaceInfo::hideEvent(QHideEvent* event)
{
    if (m_ready) {
        m_observer.reset();
        m_ready = false;
    }
    KCapacityBar::hideEvent(event);
}

void StatusBarSpaceInfo::mousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton) {
        // Creates a menu with tools that help to find out more about free
        // disk space for the given url.

        // Note that this object must live long enough in case the user opens
        // the "Configure..." dialog
        KMoreToolsMenuFactory menuFactory(QStringLiteral("dolphin/statusbar-diskspace-menu"));
        menuFactory.setParentWidget(this);
        auto menu = menuFactory.createMenuFromGroupingNames(
            { "disk-usage", "more:", "disk-partitions" }, m_url);

        menu->exec(QCursor::pos());
    }
}

void StatusBarSpaceInfo::slotValuesChanged()
{
    Q_ASSERT(m_observer);
    const quint64 size = m_observer->size();

    if (!m_shown || size == 0) {
        hide();
        return;
    }

    m_ready = true;

    const quint64 available = m_observer->available();
    const quint64 used = size - available;
    const int percentUsed = qRound(100.0 * qreal(used) / qreal(size));

    setText(i18nc("@info:status Free disk space", "%1 free", KIO::convertSize(available)));
    setUpdatesEnabled(false);
    setValue(percentUsed);
    setUpdatesEnabled(true);

    if (!isVisible()) {
        show();
    } else {
        update();
    }
}

void StatusBarSpaceInfo::drawCapacityBarNew(QPainter *p, const QRect &rectangle) const
 {
     p->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);

     p->save();

     QRect drawRect(rectangle);

     drawRect.setHeight(8);
     drawRect.moveTop(12);

     QRect textRect(rectangle);
     textRect.moveTop(-4);
     p->drawText(textRect, Qt::AlignTop | horizontalTextAlignment(), fontMetrics().elidedText(text(), Qt::ElideRight, drawRect.width()));

     QRect rect(rectangle);
     rect.moveTop(20);

     QPainterPath outline;

     outline.moveTo(rect.left() + ROUND_MARGIN / 4 + 1, rect.top());
     outline.lineTo(rect.left() + drawRect.width() - ROUND_MARGIN / 4 - 1, rect.top());
     outline.quadTo(rect.left() + drawRect.width() + ROUND_MARGIN / 2,
                    drawRect.height() / 2 + rect.top(),
                    rect.left() + drawRect.width() - ROUND_MARGIN / 4 - 1,
                    drawRect.height() + rect.top());
     outline.lineTo(rect.left() + ROUND_MARGIN / 4 + 1, drawRect.height() + rect.top());
     outline.quadTo(-ROUND_MARGIN / 2 + rect.left(), drawRect.height() / 2 + rect.top(), rect.left() + ROUND_MARGIN / 4 + 1, rect.top());
     const QColor fillColor = QColor(0x1d, 0x1d, 0x1d, 0x14);
     const QColor barColor = QColor(0x39, 0x88, 0xff);
     p->fillPath(outline, QColor(fillColor.red(), fillColor.green(), fillColor.blue(), fillColor.alpha()));

//     QRadialGradient bottomGradient(QPointF(rect.width() / 2, drawRect.bottom() + 1), rect.width() / 2);
//     bottomGradient.setColorAt(0, palette().window().color().darker(LightShade));
//     bottomGradient.setColorAt(1, Qt::transparent);
//     p->fillRect(QRect(rect.left(), drawRect.bottom() + rect.top(), rect.width(), 1), bottomGradient);

     p->translate(rect.left() + 2, rect.top() + 1);

     drawRect.setWidth(drawRect.width() - 4);
     drawRect.setHeight(drawRect.height() - 2);

     QPainterPath path;
     path.moveTo(ROUND_MARGIN / 4, 0);
     path.lineTo(drawRect.width() - ROUND_MARGIN / 4, 0);
     path.quadTo(drawRect.width() + ROUND_MARGIN / 2, drawRect.height() / 2, drawRect.width() - ROUND_MARGIN / 4, drawRect.height());
     path.lineTo(ROUND_MARGIN / 4, drawRect.height());
     path.quadTo(-ROUND_MARGIN / 2, drawRect.height() / 2, ROUND_MARGIN / 4, 0);

     QLinearGradient linearGradient(0, 0, 0, drawRect.height());
     linearGradient.setColorAt(0.5, palette().window().color().darker(MidShade));
     linearGradient.setColorAt(1, palette().window().color().darker(LightShade));
     //p->fillPath(path, linearGradient);

     p->setBrush(Qt::NoBrush);
     p->setPen(Qt::NoPen);

     if (continuous() || !fillFullBlocks()) {
         int start = (layoutDirection() == Qt::LeftToRight) ? -1 : (drawRect.width() + 2) - (drawRect.width() + 2) * (value() / 100.0);

         p->setClipRect(QRect(start, 0, (drawRect.width() + 2) * (value() / 100.0), drawRect.height()), Qt::IntersectClip);
     }

     int left = (layoutDirection() == Qt::LeftToRight) ? 0 : drawRect.width();

     int right = (layoutDirection() == Qt::LeftToRight) ? drawRect.width() : 0;

     int roundMargin = (layoutDirection() == Qt::LeftToRight) ? ROUND_MARGIN : -ROUND_MARGIN;

     int spacing = 2;
     int verticalSpacing = VERTICAL_SPACING;
     int slotWidth = 6;
     int start = roundMargin / 4;

     QPainterPath internalBar;
     internalBar.moveTo(left + roundMargin / 4, 0);
     internalBar.lineTo(right - roundMargin / 4, 0);
     internalBar.quadTo(right + roundMargin / 2, drawRect.height() / 2, right - roundMargin / 4, drawRect.height());
     internalBar.lineTo(left + roundMargin / 4, drawRect.height());
     internalBar.quadTo(left - roundMargin / 2, drawRect.height() / 2, left + roundMargin / 4, 0);

     QLinearGradient fillInternalBar(left, 0, right, 0);
     fillInternalBar.setColorAt(0, barColor);
     fillInternalBar.setColorAt(0.5, barColor);
     fillInternalBar.setColorAt(1, barColor);

     p->fillPath(internalBar, barColor);

     if (drawTextMode() == KCapacityBar::DrawTextInline) {
         p->restore();
     }

     p->save();
     p->setClipping(false);
     QRadialGradient topGradient(QPointF(rect.width() / 2, drawRect.top()), rect.width() / 2);
     const QColor fillTopColor = palette().window().color().darker(LightShade);
     topGradient.setColorAt(0, QColor(fillTopColor.red(), fillTopColor.green(), fillTopColor.blue(), 127));
     topGradient.setColorAt(1, Qt::transparent);
     p->fillRect(QRect(rect.left(), rect.top() + drawRect.top(), rect.width(), 2), topGradient);
     p->restore();
 }

void StatusBarSpaceInfo::paintEvent(QPaintEvent *event)
{
    QPainter p(this);
    p.setClipRect(event->rect());
    drawCapacityBarNew(&p, contentsRect());
    p.end();
}

